# _*_ coding:utf-8 _*_
# @Time  : 2023.06.13
# @Author: zizlee
from typing import List
from fastapi import APIRouter, Query, Body
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from response import APIResponse
from db_utils import BaseDatabaseConnection, IndexDataBaseConnection


class FormulaColumnItem(BaseModel):
    cal_value: float
    ID: int


class ReCalculateItem(BaseModel):
    fid: int
    columns: List[FormulaColumnItem]


class Formula:

    def __init__(self, group_id: int = None):
        self.gid = group_id

    def calculate_formula_result(self, formula):
        column_values = {f'ID_{col["ID"]}': col['cal_value'] for col in formula['columns']}
        expression = formula['formula']
        for k, v in column_values.items():
            expression = expression.replace(k, str(v))
        try:
            result = round(eval(expression), 4)
        except ValueError:
            result = None
        except TypeError:
            result = None
        return result

    # 获取组下公式和计算项
    def get_formula_list(self):
        sql = """
            SELECT f.id,f.group_id,f.name,f.formula,f.vcode,f.sort_num,f.unit,
                c.id As ID,c.formula_id,c.name As cname,c.is_constant,c.constant,c.is_index,c.index_id,c.unit As cunit
            FROM calcul_group_formula As f
            RIGHT JOIN calcul_formula_column As c
            ON f.id=c.formula_id
            WHERE f.is_active=1 AND f.group_id=%s
            ORDER BY f.sort_num,c.sort_num;
        """
        with BaseDatabaseConnection() as conn:
            conn.cursor.execute(sql, [self.gid])
            ret_list = list(conn.cursor.fetchall())

            tmp_dict = {}
            for r in ret_list:
                col = {k2: r[k2] for k2 in ['ID', 'cname', 'is_constant', 'constant', 'is_index', 'index_id', 'cunit']}

                col['cal_value'] = r['constant']
                col['unit'] = r['cunit']
                if col['is_index'] and col['index_id']:
                    # 查询指标数据的最新值
                    conn.cursor.execute("SELECT end_value,unit FROM datlib_index WHERE id=%s;", [col['index_id']])
                    value = conn.cursor.fetchone()
                    if value:
                        col['cal_value'] = value['end_value']
                        col['unit'] = value['unit']

                if r['id'] in tmp_dict:
                    _obj = tmp_dict[r['id']]
                    _obj['columns'].append(col)
                else:
                    tmp_dict[r['id']] = {
                        **{k: r[k] for k in ['id', 'group_id', 'name', 'formula', 'unit','vcode']},
                        'columns': [col],
                    }
        formula_list = list(tmp_dict.values())
        for f in formula_list:
            f['result'] = self.calculate_formula_result(formula=f)
            del f['formula']
            # print({k: f[k] for k in ['id', 'group_id', 'name', 'formula', 'vcode', 'result']})
            # # 计算结果值
            # for c in f['columns']:
            #     print('\t', c)
        return APIResponse.success({'data': formula_list})

    # 公式数据重新计算
    def re_calculate_formula(self, calculation: ReCalculateItem):
        with BaseDatabaseConnection() as conn:
            conn.cursor.execute("SELECT name,formula FROM calcul_group_formula WHERE id=%s LIMIT 1;",
                                [calculation.fid])
            formula = conn.cursor.fetchone()
        if not formula:
            return APIResponse.validate_error(detail='公式不存在，无法计算!')
        formula['columns'] = jsonable_encoder(calculation.columns)
        result = self.calculate_formula_result(formula=formula)
        return APIResponse.success(data={'result': result, 'name': formula['name'], 'fid': calculation.fid})


formula_api = APIRouter()


@formula_api.get('/formulaList/')  # 获取组下的公式列表
def group_formula_list(group: int = Query(...)):
    return Formula(group_id=group).get_formula_list()


@formula_api.post('/reCalculate/')  # 重新计算
def re_calculate(calculate_item: ReCalculateItem = Body(...)):
    return Formula().re_calculate_formula(calculation=calculate_item)


